package service.impl;

import DAO.NetworkDAO;
import entity.db.CitationTextDB;
import service.NetworkService;
import util.StopWordUtil;

import java.util.*;

/**
 * @author ：hzs
 * @date ：Created in 2021/1/2 14:59
 * @description：
 * @modified By：
 * @version: $
 */
public class NetworkServiceImpl implements NetworkService {

    private NetworkDAO networkDAO = new NetworkDAO();
    private List<String> visited = new ArrayList<>();

    @Override
    public List<CitationTextDB> getKnowledgeFlowNetworkAfterFilter(String whereSql) {
        List<CitationTextDB> knowledgeFlowNetworkAfterFilter = networkDAO.getKnowledgeFlowNetworkAfterFilter(whereSql);
        return knowledgeFlowNetworkAfterFilter;
    }

    @Override
    public List<LinkedList<String>> getLongestChain(List<CitationTextDB> adjacency, int topK) {
        for (int i = 1; i < 3543; i++) {
            getLongsetChainFromSource(adjacency, topK, String.valueOf(i));
        }
        return null;
    }

    @Override
    public List<LinkedList<String>> getLongsetChainFromSource(List<CitationTextDB> adjacency, int topK, String source) {
        int nextNode = Integer.valueOf(source);
        List<Integer> sign = new ArrayList<>();
        int longest = -1;
        sign.add(nextNode);
        Stack<Integer> stack = new Stack<>();
        int i = 0;
        do {
            boolean foundNextNode = false;
            for (CitationTextDB citationTextDB : adjacency) {
                if (citationTextDB.getSource() == nextNode && !sign.contains(citationTextDB.getTarget())) {
                    sign.add(nextNode);
                    stack.push(nextNode);
                    nextNode = citationTextDB.getTarget();
                    foundNextNode = true;
                    break;
                }
            }
            if (!foundNextNode) {
                sign.add(nextNode);
                int len = stack.size();
                if (longest < len) {
                    longest = len;
                }
                if (len > 3) {
                    System.out.print("" + nextNode + " ");
                    while (!stack.empty()) {
                        System.out.print("" + stack.pop() + " ");
                    }
                    System.out.println();
                }
                if (stack.empty()) {
                    break;
                } else {
                    nextNode = stack.pop();
                }
            }
            i++;
        } while (i < 1000);
        if (i > 999) {
            System.out.println("循环过千：" + source);
        }
        return null;
    }

    public void test1() {
        final String source = "3049";
        final String whereSql = " where similarity > 0.2 AND distribution != 0 and deleted = 0 group by source, target";
        List<CitationTextDB> knowledgeFlowNetworkAfterFilter = getKnowledgeFlowNetworkAfterFilter(whereSql);
//        getLongsetChainFromSource(knowledgeFlowNetworkAfterFilter, 5, source);
        getLongestChain(knowledgeFlowNetworkAfterFilter, 5);
    }

    /**
     * 发现每个社区的研究方向
     */
    public void getEachCommunityResearchContent() {
        // 212 176 288 137 7 189 49 298 covid-19 307
        List<List<Object>> allTitle = networkDAO.executeReadSql("select title, communityid from paper where communityid is not null");
        List<List<Object>> lists = networkDAO.executeReadSql("select title from paper where communityid = " +
                "307");
        HashMap<String, Double> map = new HashMap<>();
        String reg = "[^0-9a-zA-Z ]";
        Set<String> set = StopWordUtil.getStopWordsEN();
        for (List<Object> list : lists) {
            String title = list.get(0).toString();
            String[] split = title.replaceAll(reg,"").split(" ");
            for (String s : split) {
                if (!set.contains(s)) {
                    if (map.containsKey(s)) {
                        map.put(s, map.get(s) + 1);
                    } else {
                        map.put(s, 1.0);
                    }
                }
            }
        }
        HashMap<String, Double> newMap = new HashMap<>(map.size());
        Set<Integer> set1 = null;
        for (Map.Entry<String, Double> entry : map.entrySet()) {
            set1 = new HashSet<>();
            String key = entry.getKey();
            int idf = 0;
            for (List<Object> list : allTitle) {
                int communityId = (Integer) list.get(1);
                if (((String) list.get(0)).contains(key) && !set1.contains(communityId)) {
                    idf++;
                    set1.add(communityId);
                }
            }
            newMap.put(key, map.get(key) * Math.log(335/(idf+1)));
        }
        ArrayList<Map.Entry<String, Double>> entries = new ArrayList<>(newMap.entrySet());
        Collections.sort(entries, (a, b) -> b.getValue().compareTo(a.getValue()));
        System.out.println();
    }

    /**
     * 根据文章出版日期来发现错误的引用关系
     */
    public void findWrongReferByDate() {
        String sql = "SELECT id, publish_date FROM paper";
        List<List<Object>> lists = networkDAO.executeReadSql(sql);
        Map<String, String> map = new HashMap<>(lists.size(), 1.0F);
        for (List<Object> list : lists) {
            String id = list.get(0).toString();
            String date = convertDate((String)list.get(1));
            map.put(id, date);
        }

        sql = "SELECT paperid, referedid FROM referencestable t " +
                "where t.`unavailable_paper` is null";
        lists = networkDAO.executeReadSql(sql);
        for (List<Object> list : lists) {
            String source = (String) list.get(0);
            String target = (String) list.get(1);
            if (map.get(source).compareTo(map.get(target)) == -1) {
                System.out.println(source + " -> " + target);
            }
        }
    }

    public String convertDate(String date) {
        Map<String, String> months = new HashMap<>(12, 1.00F);
        months.put("January", "01");
        months.put("February", "02");
        months.put("March", "03");
        months.put("April", "04");
        months.put("May", "05");
        months.put("June", "06");
        months.put("July", "07");
        months.put("August", "08");
        months.put("September", "09");
        months.put("October", "10");
        months.put("November", "11");
        months.put("December", "12");
        String[] split = date.split(", ");
        String year = split[1];
        String month = months.get(split[0].split(" ")[0]);
        String day = split[0].split(" ")[1];
        return year+month+day;
    }


    public void findLongestChainInCommunity(int communityId) {
        String sql = "SELECT paperid, referedid FROM referencestable t " +
                "INNER JOIN (SELECT id FROM paper WHERE communityid = " +
                communityId +") AS a ON t.`paperid` = a.`id` " +
                "INNER JOIN (SELECT id FROM paper WHERE communityid = " +
                communityId + ") AS b ON t.`referedid` = b.`id` " +
                "where t.`unavailable_paper` is null and t.`similarity` < 1";
        String sql2 = "SELECT paperid, referedid FROM referencestable t " +
//                "INNER JOIN (SELECT id FROM paper WHERE RIGHT(publish_date, 4) > 2019 AND (title LIKE \"%covid-19%\" OR title LIKE \"2019-ncov\" OR title LIKE \"%2019 novel coronavirus%\" OR title LIKE \"%SARS-CoV-2%\" OR title LIKE \"%ncov-19%\")) AS a ON t.`paperid` = a.`id` " +
//                "INNER JOIN (SELECT id FROM paper WHERE RIGHT(publish_date, 4) > 2019 AND (title LIKE \"%covid-19%\" OR title LIKE \"2019-ncov\" OR title LIKE \"%2019 novel coronavirus%\" OR title LIKE \"%SARS-CoV-2%\" OR title LIKE \"%ncov-19%\")) AS b ON t.`referedid` = b.`id` " +
                "where t.`unavailable_paper` is null and t.`similarity` < 1";
        List<List<Object>> lists = networkDAO.executeReadSql(sql2);
//        Set<String> nodes = new HashSet<>();
        List<Arc> arcs = new ArrayList<>();
        Map<String, String> nodeChain = new HashMap<>();
        for (List<Object> list : lists) {
            String source = (String) list.get(0);
            String target = (String) list.get(1);
            Arc arc = new Arc();
            arc.source = source;
            arc.target = target;
            arcs.add(arc);
            nodeChain.put(source, null);
            nodeChain.put(target, null);
        }
        for (String s : nodeChain.keySet()) {
            String loop = findLoop(arcs, s);
            if (!"".equals(loop)) {
                continue;
//                System.out.println(loop);
            }
//            String s = "4215";
            String longestChain = findLongestChain(arcs, nodeChain, s);
            if (longestChain.split("->").length > 15) {
                System.out.println(longestChain);
            }
        }
    }

    public String findLongestChain(List<Arc> arcs, Map<String, String> nodeChain, String start) {
        String chain = nodeChain.get(start);
        if (chain == null) {
            List<String> nexts = new ArrayList<>();
            // 找出后继节点
            for (Arc arc : arcs) {
                if (arc.source.equals(start)) {
                    nexts.add(arc.target);
                }
            }
            // 如果后继无人
            if (nexts.isEmpty()) {
                nodeChain.put(start, start);
                chain = start;
            }
            // 遍历后继节点，找出最长的
            int len = Integer.MIN_VALUE;
            for (String next : nexts) {
                findLongestChain(arcs, nodeChain, next);
                String nextChain = nodeChain.get(next);
                int length = nextChain.split("->").length;
                if (length > len) {
                    len = length;
                    chain = start.concat("->").concat(nextChain);
                }
            }
            nodeChain.put(start, chain);
        }
//        System.out.println(chain);
        return chain;
    }

    public String findLoop(List<Arc> arcs, String start) {
        Set<String> nodes = new HashSet<>();
        if (visited.contains(start)) {
            String result = start;
            for (int i = visited.size() - 1; i >= 0; i--) {
                String s = visited.get(i);
                if (s.equals(start)) {
                    return result;
                } else {
                    result = result +  "," + s;
                }
            }
        }
        visited.add(start);
        // 找出后继节点
        for (Arc arc : arcs) {
            if (arc.source.equals(start)) {
                String loop = findLoop(arcs, arc.target);
                if (!"".equals(loop)) {
                    return loop;
                }
            }
        }
        visited.remove(start);
        return "";
    }

    static class Arc {
        String source;
        String target;
        boolean visited = false;
    }

    public static void main(String[] args) {
        NetworkServiceImpl networkService = new NetworkServiceImpl();
//        networkService.findWrongReferByDate();
        networkService.findLongestChainInCommunity(212);
    }
}
